package com.showapi.app.service

import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.showapi.app.dynamic.BaseHandler
import com.showapi.app.dynamic.BaseLine
import com.showapi.app.util.CommonUtil
import com.showapi.app.util.ConstantsOfShowapi
import groovy.io.FileType
import org.apache.commons.lang3.StringUtils
import org.springframework.context.annotation.Configuration
import org.springframework.core.io.ClassPathResource

@Configuration
class FlowService extends BaseService {

	def dirPath="springboot${File.separator}line_of_flow".toString()
	def service_path
	def allClassCacheMap=[:]//存放所有类 ,key是文件的path,value是[md5:"",clazz:aClass]
	def lineClassCacheMap=[:]
	def lineInstanceCacheMap =[:]  //实例池
	def ShowapiClassLoader showapiClassLoader

	//后台调用，比如定时任务
	def run_backgroud={lineName, para=[:]->
        if(ConstantsOfShowapi.IS_DEV) { //开发环境，则每次请求都reloadClass所有类，改过的文件才会编译
            reloadClass()
        }
        boolean can_go=true
		for(int i=0;i<10;i++){
			if(ConstantsOfShowapi.flowServiceLockMap.get(lineName)){
				can_go=false
				Thread.sleep(500)
			}else{
				can_go=true
				break
			}
		}
        if(!can_go){
            return getRetObj(-1,'渠道繁忙，请稍候再试')
        }

		log.info "run_backgroud   "+lineName+"   "
		def item=lineInstanceCacheMap.get(lineName)
		if(!item){
			return  getRetObj(-1,'找不到执行lineName')
		}
		def instance=item.instance
		if(para==null)para=[:]
		def ret
		try{
			ret=instance.run(para)
			println "----------------------"
		}catch(Exception e){
			e.printStackTrace()
			ret= getRetObj(-1,'方法调用出错')
		}
		return ret

	}




	//通过类名获取一个实例
	public BaseLine getInstance(className){
		return lineInstanceCacheMap.get(className)?.instance
	}

	public void  reloadClass(classNameList=null){
		try{
			init()
			checkParentModify()
			def temp_map=[:]
			if(classNameList){	//将其转换为map
				classNameList= JSON.parseArray(classNameList)
				classNameList.each{
					temp_map.put(it,1)
				}
			}
			new File(service_path).eachFileRecurse(FileType.FILES){ File file->
				def path=file.getAbsolutePath()
                if(!path.endsWith(".groovy"))return

				int ind=path.indexOf(dirPath)
				int end=path.lastIndexOf(".groovy")
				if(end==-1)return
				def className=path.substring(ind+dirPath.size()+1,end)  //+1是把斜线也算进去了
				className=StringUtils.replace(className, "\\",".")
				className=StringUtils.replace(className, "/",".")
				if(temp_map&&!temp_map.get(className)){  //不是指定的className,返回
					return
				}
				ConstantsOfShowapi.flowServiceLockMap.put(className,1)
				println ".............go into  load class "+className
				try{
					showapiClassLoader.loadClass(className)  //里面会递归加载import，extends及自身三样
				}finally{
					ConstantsOfShowapi.flowServiceLockMap.remove(className)
					println "::::::::::::::leave  load class "+className
				}

			}
		}catch(Exception e){
			e.printStackTrace()
		}
	}



	public void  reloadNodeClass(classNameList=null){
		def temp_map=[:]
		if(classNameList){	//将其转换为map
			classNameList= JSON.parseArray(classNameList)
			classNameList.each{
				temp_map.put(it,1)
			}
		}
		try{
			lineInstanceCacheMap.each{k,line->
				temp_map.each{className,temp->
					if(line.instance.nodeInstanceCacheMap.get(className)){
						line.instance.reloadClass()
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace()
		}
	}

	public init(){
        if(showapiClassLoader)return
        ClassLoader parent = getClass().getClassLoader();
        if(ConstantsOfShowapi.IS_DEV){//需要找到classpath
			def filepath=new File("").getAbsolutePath()
			service_path="${filepath}/src/main/resources/static/${dirPath}/".toString()
			log.info "in   DEVELOPMENT env,service_path is :"+service_path

		}else{
			ClassPathResource resource = new ClassPathResource("application.properties" );
			def filepath=new File(resource.getFile().getParentFile(),"static").getAbsolutePath() //向上一级
			service_path="${filepath}/${dirPath}/".toString()
			log.info "in   PRODUCTION env,service_path is :"+service_path
        }
        showapiClassLoader=new ShowapiClassLoader(parent);
    }
	//检查父级或工具类改变情况。如果改变，则全体重新编译
	public void checkParentModify(){
		boolean needAllReload=false
		allClassCacheMap.find{className,item->  //必须要全部重新加载
			println "aaaaaaaaaaaaaaaaaaaaaa   "+className

			if(item.isParentOrUtil){//只检查父级或工具类
				def f=new File(item.filePath)
				println "bbbbbbbbbbbbbbbbbb   "+f.getAbsolutePath()

				def src=f.getText("utf-8")
				def now_md5=src.md5()
				if(item.md5!=now_md5){
					needAllReload=true //标记为需要全部重新加载
					return 1
				}
			}
		}

		log.info "needAllReload  is(1 step of flow): "+needAllReload
		if(needAllReload){
			allClassCacheMap.clear()
		}

	}

	//加载父类
	public   loadParentClass={src->
		int ind=src.indexOf("{")  //读到这个标记认为引用头结束
		src=src.substring(0,ind)
		def  pack_name
		def tempMap=[:]//用于把import加载过的类，不再extend中加载
		src.eachLine{
			def line=it.trim()
			if(!line)return
			if(line.startsWith("//"))return
			if(line.contains("*"))return
			if(!pack_name){
				line.find(/(?is)\s?package\s+([\w\.]+)/){mat,pa->
					pack_name=pa
				}
				if(pack_name)return  //找到就返回
			}

			boolean is_import=false
			line.find(/(?is).*?import\s+([\w\.]+)/){mat,className->
//				log.info "loadCommonClass    "+className
				is_import=true
				showapiClassLoader.loadClass(className)  //加载import
			}
			if(!is_import){ //加载extend
				line.find(/(?is)\s?class\s+[\w]+\s+extends\s+([\w\.]+)/){mat,className->
					if(!className.contains(".")){//不是全名
						className=pack_name+"."+className
					}
					showapiClassLoader.loadClass(className)
					def item=lineClassCacheMap.get(className)
					if(item){
						lineClassCacheMap.remove(className) //extend的类不出现在此列表里
						lineInstanceCacheMap.remove(className)
						item.isParentOrUtil=true  //是父级或工具类
					}

				}
			}
		}
	}

	public  class  ShowapiClassLoader extends  ClassLoader{
		public ShowapiClassLoader(parentLoader){
			super(parentLoader)
		}
		protected Class<?> findClass(String className)
				throws ClassNotFoundException {
			def service=FlowService.this
			def pack_path=className.replaceAll("\\.","/")
			def filePath=service.service_path+pack_path+".groovy"
			def f=new File(filePath)
			if(!f.exists()){return}
			def item=service.allClassCacheMap.get(className)

			def src
			def modify_time=f.lastModified()

			if(!item){//如果没加载过
				src=f.getText("utf-8")
				service.loadParentClass(src)  //先加载父类
			}else{
				if(item.modify_time==modify_time){	//时间没改过，直接返回
					println "same modify_time ,find in cache: "+item.clazz
					if(BaseLine.class.isAssignableFrom(item.clazz)){//排除工具类
						item.instance.reloadClass( )
					}
					return item.clazz
				}
				//运行到这里，说明文件肯定改过
				src=f.getText("utf-8")

			}
			def now_md5=src.md5()
			Class clazz
			if(item?.md5==now_md5) {	//时间改变过，但内容没变
				println "different modify_time,but same content ,find in cache: "+item.clazz
				item.modify_time=modify_time
				if(BaseLine.class.isAssignableFrom(item.clazz)){//有可能是工具类
					item.instance.reloadClass( )
				}
				return item.clazz

			}else{//没加载过，或内容不同
				try{
					GroovyClassLoader onceClassLoader=new GroovyClassLoader(this )
					println("parse file "+className)
					clazz=onceClassLoader.parseClass(f)
					item=[md5:now_md5,clazz:clazz,filePath:filePath,modify_time:modify_time]
					if(BaseLine.class.isAssignableFrom(clazz)){ //放入line实例缓存
						service.lineClassCacheMap.put(className,item)
						def instance=clazz.newInstance()
						instance.ctx=ConstantsOfShowapi.ctx
						instance.reloadClass()  //这里是 lineObj调用reload
						item.instance=instance
						lineInstanceCacheMap.put(className,item)   //不能直接放instance，是因为要对item做reload引用
					}else{
						item.isParentOrUtil=true  //是父级或工具类
					}
					service.allClassCacheMap.put(className,item)

				}catch(Exception e){
					e.printStackTrace()
					log.error("reloadClass  err")
				}

			}
			return clazz
		}

	}
}
